"""
Agent Publish Service
Handles publishing and unpublishing agents with API key management
"""

import os
import sys
import time
from pathlib import Path
from typing import Dict, Any, List, Optional

# Add project root to path
project_root = Path(__file__).parent.parent.parent
sys.path.insert(0, str(project_root))

from src.llm.hiagent_app.utils.api_client import HiAgentAPIClient
from src.llm.hiagent_app.config.hiagent_config import HiAgentConfig


class AgentPublishService:
    """Service for publishing and unpublishing agents"""

    def __init__(self, workspace_id: Optional[str] = None):
        """
        Initialize the publish service

        Args:
            workspace_id: Workspace ID, defaults to config value
        """
        self.client = HiAgentAPIClient()
        self.workspace_id = workspace_id or HiAgentConfig.DEFAULT_WORKSPACE_ID

    def list_api_keys(self, app_id: str) -> List[Dict[str, Any]]:
        """
        List all API keys for an agent

        Args:
            app_id: Agent ID

        Returns:
            List of API key info dictionaries
        """
        request_data = {
            "WorkspaceID": self.workspace_id,
            "AppID": app_id,
            "ApiKeyType": "api",
            "Top": {}
        }

        result = self.client.make_request(
            action="ListAppAPIKey",
            body=request_data
        )

        # Parse response
        if "Response" in result:
            if result.get("Response", {}).get("Error"):
                error = result["Response"]["Error"]
                raise Exception(f"Failed to list API keys: {error.get('Code')} - {error.get('Message')}")
            return result["Response"].get("AppAPIKeyInfos", [])
        else:
            return result.get("AppAPIKeyInfos", [])

    def delete_api_key(self, api_key_id: str) -> bool:
        """
        Delete an API key

        Args:
            api_key_id: API Key ID to delete

        Returns:
            True if successful
        """
        request_data = {
            "WorkspaceID": self.workspace_id,
            "APIKeyID": api_key_id,
            "Top": {}
        }

        result = self.client.make_request(
            action="DeleteAppAPIKey",
            body=request_data
        )

        # Check for errors
        if "Response" in result:
            if result.get("Response", {}).get("Error"):
                error = result["Response"]["Error"]
                raise Exception(f"Failed to delete API key: {error.get('Code')} - {error.get('Message')}")

        return True

    def create_api_key(
        self,
        app_id: str,
        description: str = "Published API Key",
        expired: str = "forever"
    ) -> Dict[str, str]:
        """
        Create a new API key

        Args:
            app_id: Agent ID
            description: Description for the API key
            expired: Expiration time (forever, 1d, 7d, 1m, etc.)

        Returns:
            Dictionary with api_key and api_key_id
        """
        request_data = {
            "WorkspaceID": self.workspace_id,
            "AppID": app_id,
            "Expired": expired,
            "Description": description,
            "ApiKeyType": "api",
            "Top": {}
        }

        result = self.client.make_request(
            action="CreateAppAPIKey",
            body=request_data
        )

        # Parse response
        if "Response" in result:
            if result.get("Response", {}).get("Error"):
                error = result["Response"]["Error"]
                raise Exception(f"Failed to create API key: {error.get('Code')} - {error.get('Message')}")
            response = result["Response"]
        else:
            response = result

        api_key = response.get("APIKey") or response.get("ApiKey")
        api_key_id = response.get("APIKeyID") or response.get("AppKeyID") or response.get("ApiKeyID")

        if not api_key or not api_key_id:
            raise Exception("Failed to extract API key from response")

        return {
            "api_key": api_key,
            "api_key_id": api_key_id
        }

    def _ensure_model_config_defaults(self, app_config: Dict[str, Any]) -> Dict[str, Any]:
        """
        Ensure ModelConfig has all required fields with default values

        Args:
            app_config: The app configuration dictionary

        Returns:
            Updated app_config with complete ModelConfig
        """
        if not app_config:
            return app_config

        model_config = app_config.get("ModelConfig", {})

        # Ensure all required fields have default values
        defaults = {
            "Temperature": 0.7,
            "TopP": 0.9,
            "MaxTokens": 2000,
            "RoundsReserved": 10,
            "RagNum": 5,
            "Strategy": "react",
            "MaxIterations": 5,
            "RagEnabled": False,
            "ReasoningMode": False,
            "ReasoningSwitch": False,
            "ReasoningSwitchType": "disabled",  # Required field
            "IsAdvancedMode": False,
            "CurrentTimeEnabled": False
        }

        # Merge defaults with existing config
        for key, default_value in defaults.items():
            if key not in model_config or model_config[key] is None or model_config[key] == "":
                model_config[key] = default_value

        app_config["ModelConfig"] = model_config
        return app_config

    def publish_agent(
        self,
        app_id: str,
        version: str = "1.0.0",
        version_desc: str = "Published via API",
        category_codes: List[str] = None,
        auth_type: str = "Public",
        auth_range_type: str = "All"
    ) -> Dict[str, Any]:
        """
        Publish agent to AppCenter

        Args:
            app_id: Agent ID
            version: Version number
            version_desc: Version description
            category_codes: Category codes list
            auth_type: Authorization type (Public/Private)
            auth_range_type: Authorization range (All/Partial)

        Returns:
            Publish result dictionary
        """
        if category_codes is None:
            category_codes = ["WorkAssistant"]

        # Get draft config
        config_result = self.client.make_request(
            action="GetAppConfigDraft",
            body={
                "WorkspaceID": self.workspace_id,
                "AppID": app_id,
                "Top": {}
            }
        )

        if "Response" in config_result:
            config_draft = config_result["Response"]
        else:
            config_draft = config_result

        agent_mode = config_draft.get("DraftAgentMode", "Single")

        # Build publish request
        request_data = {
            "WorkspaceID": self.workspace_id,
            "AppID": app_id,
            "Version": version,
            "VersionDescription": version_desc,
            "AgentMode": agent_mode,
            "PublishAppCenter": {
                "IsPublish": True,
                "AuthType": auth_type,
                "CategoryCodeList": category_codes,
                "AuthRangeType": auth_range_type
            },
            "Top": {}
        }

        # Add config based on agent mode
        if agent_mode == "Single":
            app_config = config_draft.get("AppConfigDraft", {})
            if app_config:
                # Ensure all required ModelConfig fields have defaults
                app_config = self._ensure_model_config_defaults(app_config)
                request_data["AppConfig"] = app_config
        elif agent_mode == "Multi":
            multi_config = config_draft.get("MultiAgentConfigDraft", {})
            if multi_config:
                request_data["MultiAgentConfigInfo"] = multi_config

        result = self.client.make_request(
            action="PublishApp",
            body=request_data
        )

        # Check for approval created
        if "ResponseMetadata" in result:
            metadata = result["ResponseMetadata"]
            error = metadata.get("Error", {})

            if error.get("Code") == "ApprovalCreated":
                approval_id = error.get("Message", "").split(": ")[-1]
                if not approval_id or approval_id == error.get("Message"):
                    approval_data = error.get("Data", {})
                    approval_id = approval_data.get("__Message.ApprovalID", approval_id)

                return {
                    "approval_id": approval_id,
                    "status": "pending_approval",
                    "message": "Approval created, awaiting approval"
                }

        # Parse response
        if "Response" in result:
            if result.get("Response", {}).get("Error"):
                error = result["Response"]["Error"]
                raise Exception(f"Failed to publish: {error.get('Code')} - {error.get('Message')}")
            response = result["Response"]
        else:
            response = result

        publish_id = response.get("PublishID") or response.get("ID")

        return {
            "publish_id": publish_id,
            "status": "published",
            "message": "Agent published successfully"
        }

    def auto_approve(self, approval_id: str, message: str = "Auto-approved via API") -> Dict[str, Any]:
        """
        Automatically approve a publish request

        Args:
            approval_id: Approval ID to approve
            message: Approval message

        Returns:
            Approval result dictionary
        """
        request_data = {
            "ID": approval_id,
            "Message": message,
            "Top": {}
        }

        result = self.client.make_request(
            action="ApproveApproval",
            body=request_data
        )

        # Handle response (may be empty for success)
        if result is None:
            return {"success": True, "approval_id": approval_id}

        if "Response" in result and result.get("Response", {}).get("Error"):
            error = result["Response"]["Error"]
            raise Exception(f"Failed to approve: {error.get('Code')} - {error.get('Message')}")

        return {"success": True, "approval_id": approval_id}

    def wait_for_publish_complete(
        self,
        app_id: str,
        timeout: int = 120,
        check_interval: int = 5
    ) -> bool:
        """
        Wait for publish to complete by checking publish history

        Args:
            app_id: Agent ID
            timeout: Timeout in seconds
            check_interval: Check interval in seconds

        Returns:
            True if published successfully, False otherwise
        """
        start_time = time.time()

        while time.time() - start_time < timeout:
            # Get publish history
            result = self.client.make_request(
                action="ListAppPublishHistory",
                body={
                    "WorkspaceID": self.workspace_id,
                    "AppID": app_id,
                    "Filter": {}
                }
            )

            # Parse response
            if "Response" in result:
                items = result["Response"].get("Items", [])
            else:
                items = result.get("Items", [])

            if items:
                latest = items[0]
                published_channels = latest.get("PublishedChannels", [])

                # Check if published to any channel
                if len(published_channels) > 0:
                    return True

            # Wait before next check
            time.sleep(check_interval)

        return False

    def publish_with_new_key(
        self,
        app_id: str,
        delete_old_keys: bool = True,
        version: str = "1.0.0",
        version_desc: str = "Published with new API key",
        api_key_description: str = "Published API Key",
        auto_approve: bool = False,
        wait_approval: bool = False,
        approval_timeout: int = 120,
        enable_api: bool = True
    ) -> Dict[str, Any]:
        """
        Publish agent with new API key and optionally delete old keys

        Args:
            app_id: Agent ID
            delete_old_keys: Whether to delete old API keys
            version: Version number
            version_desc: Version description
            api_key_description: Description for new API key
            auto_approve: Automatically approve the publish request
            wait_approval: Wait for publish to complete
            approval_timeout: Timeout for waiting approval completion (seconds)
            enable_api: Enable API status after publish (default: True)

        Returns:
            Dictionary with app_id, new_api_key, new_api_key_id, and publish_result
        """
        deleted_keys = []

        # Step 1: Delete old API keys if requested
        if delete_old_keys:
            existing_keys = self.list_api_keys(app_id)
            for key_info in existing_keys:
                api_key_id = key_info.get('APIKeyID') or key_info.get('AppKeyID')
                if api_key_id:
                    try:
                        self.delete_api_key(api_key_id)
                        deleted_keys.append(api_key_id)
                    except Exception as e:
                        print(f"Warning: Failed to delete key {api_key_id}: {str(e)}")

        # Step 2: Create new API key
        new_key_info = self.create_api_key(app_id, description=api_key_description)

        # Step 3: Publish agent
        publish_result = self.publish_agent(app_id, version=version, version_desc=version_desc)

        # Step 4: Handle approval if needed
        approval_success = False
        api_enabled = False

        if publish_result["status"] == "pending_approval":
            approval_id = publish_result.get("approval_id")

            if auto_approve and approval_id:
                # Wait a bit for approval to be ready
                time.sleep(2)

                try:
                    # Auto-approve the publish request
                    self.auto_approve(approval_id)
                    approval_success = True

                    # If wait_approval is also True, wait for publish to complete
                    if wait_approval:
                        approval_success = self.wait_for_publish_complete(
                            app_id,
                            timeout=approval_timeout
                        )

                        if approval_success:
                            publish_result["status"] = "published"
                            publish_result["message"] = "Agent published successfully after auto-approval"
                        else:
                            publish_result["status"] = "approval_timeout"
                            publish_result["message"] = "Auto-approval succeeded but publish timeout"

                except Exception as e:
                    print(f"Warning: Auto-approval failed: {str(e)}")
                    publish_result["message"] = f"Approval created but auto-approval failed: {str(e)}"

            elif wait_approval:
                # Just wait for manual approval
                approval_success = self.wait_for_publish_complete(
                    app_id,
                    timeout=approval_timeout
                )

                if approval_success:
                    publish_result["status"] = "published"
                    publish_result["message"] = "Agent published successfully after approval"
                else:
                    publish_result["status"] = "approval_timeout"
                    publish_result["message"] = "Waiting for approval timeout"

        elif publish_result["status"] == "published":
            approval_success = True

        # Step 5: Enable API if requested and publish was successful
        if enable_api and approval_success:
            try:
                self.update_api_runnable(app_id, enable=True)
                api_enabled = True
                publish_result["message"] += " (API enabled)"
            except Exception as e:
                print(f"Warning: Failed to enable API: {str(e)}")
                publish_result["message"] += f" (API enable failed: {str(e)})"

        return {
            "app_id": app_id,
            "new_api_key": new_key_info["api_key"],
            "new_api_key_id": new_key_info["api_key_id"],
            "deleted_keys_count": len(deleted_keys),
            "deleted_keys": deleted_keys,
            "publish_status": publish_result["status"],
            "publish_message": publish_result["message"],
            "publish_id": publish_result.get("publish_id"),
            "approval_id": publish_result.get("approval_id"),
            "approval_success": approval_success,
            "api_enabled": api_enabled
        }

    def update_api_runnable(self, app_id: str, enable: bool = True) -> bool:
        """
        Update API runnable status (enable/disable API)

        Args:
            app_id: Agent ID
            enable: Whether to enable API (default: True)

        Returns:
            True if successful

        Raises:
            Exception: If update fails
        """
        request_data = {
            "WorkspaceID": self.workspace_id,
            "AppID": app_id,
            "Enable": enable,
            "Top": {}
        }

        result = self.client.make_request(
            action="UpdateAppAPIRunnable",
            body=request_data
        )

        # Check for errors
        if "Response" in result:
            if result.get("Response", {}).get("Error"):
                error = result["Response"]["Error"]
                raise Exception(f"Failed to update API status: {error.get('Code')} - {error.get('Message')}")

        return True

    def unpublish_agent(self, app_id: str, delete_api_keys: bool = True) -> Dict[str, Any]:
        """
        Unpublish an agent by disabling/deleting its API keys

        Args:
            app_id: Agent ID
            delete_api_keys: Whether to delete all API keys (default: True)

        Returns:
            Result dictionary with deleted keys info
        """
        deleted_keys = []

        if delete_api_keys:
            # Get all API keys for this agent
            existing_keys = self.list_api_keys(app_id)

            # Delete all API keys
            for key_info in existing_keys:
                api_key_id = key_info.get('APIKeyID') or key_info.get('AppKeyID')
                if api_key_id:
                    try:
                        self.delete_api_key(api_key_id)
                        deleted_keys.append(api_key_id)
                    except Exception as e:
                        print(f"Warning: Failed to delete key {api_key_id}: {str(e)}")

        return {
            "app_id": app_id,
            "status": "unpublished",
            "message": f"Agent unpublished - {len(deleted_keys)} API key(s) deleted",
            "deleted_keys_count": len(deleted_keys),
            "deleted_keys": deleted_keys
        }
